﻿/*******************************************************************************
Copyright (c) 2011-2013, PKStudio
Copyright (c) 2011,      Zynga Inc.
Copyright (c) 2008-2010, Ricardo Quesada

http://pkstudio.org

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*******************************************************************************/

using System;
using System.Drawing;

namespace PKEngine
{
	/// <summary>
	/// PKLayer is a subclass of PKNode that can reveive input events.
	/// </summary>
	public class PKLayer : PKNode, IPKMouseResponder, IPKKeybdResponder
	{
		#region Private Fields
		private bool isMouseEnabled;
		private bool isKeybdEnabled;
		private int mousePriority;
		private int keybdPriority;
		#endregion

		#region Public Properties
		/// <summary>
		/// If the mouse events will be handled.
		/// </summary>
		public bool IsMouseEnabled
		{
			get
			{
				return this.isMouseEnabled;
			}
			set
			{
				if (this.isMouseEnabled != value)
				{
					this.isMouseEnabled = value;
					if (this.IsRunning)
					{
						if (this.isMouseEnabled)
						{
							this.RegisterWithMouseDispatcher();
						}
						else
						{
							PKMouseDispatcher.SharedDispatcher.RemoveHandler(this);
						}
					}
				}
			}
		}

		/// <summary>
		/// If the keyboard events will be handled.
		/// </summary>
		public bool IsKeybdEnabled
		{
			get
			{
				return this.isKeybdEnabled;
			}
			set
			{
				if (this.isKeybdEnabled != value)
				{
					this.isKeybdEnabled = value;
					if (this.IsRunning)
					{
						if (this.isKeybdEnabled)
						{
							this.RegisterWithKeybdDispatcher();
						}
						else
						{
							PKKeybdDispatcher.SharedDispatcher.RemoveHandler(this);
						}
					}
				}
			}
		}

		public int MousePriority
		{
			get
			{
				return this.mousePriority;
			}
			set
			{
				if (this.mousePriority != value)
				{
					this.mousePriority = value;

					if (this.IsMouseEnabled)
					{
						this.IsMouseEnabled = false;
						this.IsMouseEnabled = true;
					}
				}
			}
		}

		public int KeybdPriority
		{
			get
			{
				return this.keybdPriority;
			}
			set
			{
				if (this.keybdPriority != value)
				{
					this.keybdPriority = value;

					if (this.IsKeybdEnabled)
					{
						this.IsKeybdEnabled = false;
						this.IsKeybdEnabled = true;
					}
				}
			}
		}
		#endregion

		#region Initialization
		public PKLayer()
			: base()
		{
			this.ContentSize = new Size(PKConfig.SharedConfig.ScreenWidth, PKConfig.SharedConfig.ScreenHeight);
			this.isMouseEnabled = false;
			this.mousePriority = 0;
			this.isKeybdEnabled = false;
			this.keybdPriority = 0;
		}
		#endregion

		#region Protected Methods
		/// <summary>
		/// Register layer to MouseDispatcher.
		/// </summary>
		protected virtual void RegisterWithMouseDispatcher()
		{
			PKMouseDispatcher.SharedDispatcher.AddHandler(this, this.mousePriority, false);
		}

		/// <summary>
		/// Register layer to KeybdDispatcher.
		/// </summary>
		protected virtual void RegisterWithKeybdDispatcher()
		{
			PKKeybdDispatcher.SharedDispatcher.AddHandler(this, this.keybdPriority, false);
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// This method will be call when this layer is going to enter the "stage".
		/// </summary>
		public override void OnEnter()
		{
			// Register parent nodes first.
			// Since events are propagated in reverse order.
			if (this.IsMouseEnabled)
			{
				this.RegisterWithMouseDispatcher();
			}

			if (this.IsKeybdEnabled)
			{
				this.RegisterWithKeybdDispatcher();
			}

			base.OnEnter();
		}

		/// <summary>
		/// This method will be call when this layer is going to exit the "stage".
		/// </summary>
		public override void OnExit()
		{
			if (this.IsMouseEnabled)
			{
				PKMouseDispatcher.SharedDispatcher.RemoveHandler(this);
			}

			if (this.IsKeybdEnabled)
			{
				PKKeybdDispatcher.SharedDispatcher.RemoveHandler(this);
			}

			base.OnExit();
		}

		/// <summary>
		/// Mouse down event.
		/// </summary>
		/// <param name="mouse">the mouse</param>
		/// <returns>If we have claimed the mouse</returns>
		public virtual bool MouseDown(PKMouseEvent mouse)
		{
			// Override me.
			return true;
		}

		/// <summary>
		/// Mouse drag event.
		/// </summary>
		/// <param name="mouse">the mouse</param>
		public virtual void MouseDrag(PKMouseEvent mouse)
		{
			// Override me.
		}

		/// <summary>
		/// Mouse up event.
		/// </summary>
		/// <param name="mouse">the mouse</param>
		public virtual void MouseUp(PKMouseEvent mouse)
		{
			// Override me.
		}

		public virtual bool MouseMove(PKMouseEvent mouse)
		{
			// Override me.
			return false;
		}

		/// <summary>
		/// Key down event.
		/// </summary>
		/// <param name="keybd">the keybd</param>
		public virtual bool KeyDown(PKKeybdEvent keybd)
		{
			// Override me.
			return true;
		}

		/// <summary>
		/// Key up event.
		/// </summary>
		/// <param name="keybd">the keybd</param>
		public virtual void KeyUp(PKKeybdEvent keybd)
		{
			// Override me.
		}
		#endregion
	}

	public class PKLayerRgba : PKLayer, IPKRgba
	{
		#region Private Fields
		private int realOpacity;
		private int realColorOpacity;
		private ushort realColor;
		#endregion

		#region Properties
		/// <summary>
		/// Opacity of PKLayerRGBA.
		/// Range: 0 ~ 256
		/// </summary>
		public int Opacity
		{
			get
			{
				return this.realOpacity;
			}
			set
			{
				if (value < 0)
				{
					value = 0;
				}
				else if (value > 256)
				{
					value = 256;
				}
				this.DisplayedOpacity = this.realOpacity = value;

				if (this.CascadeOpacityEnabled)
				{
					int parentOpacity = 256;
					if (this.Parent != null && this.Parent is IPKRgba && ((IPKRgba)this.Parent).CascadeOpacityEnabled)
					{
						parentOpacity = ((IPKRgba)this.Parent).DisplayedOpacity;
					}
					this.UpdateDisplayedOpacity(parentOpacity);
				}
			}
		}

		/// <summary>
		/// ColorOpacity of PKLayerRGBA.
		/// Range: 0 ~ 256
		/// </summary>
		public int ColorOpacity
		{
			get
			{
				return this.realColorOpacity;
			}
			set
			{
				if (value < 0)
				{
					value = 0;
				}
				else if (value > 256)
				{
					value = 256;
				}
				this.DisplayedColorOpacity = this.realColorOpacity = value;

				if (this.CascadeColorEnabled)
				{
					ushort parentColor = PKAlpha.SharedAlpha.ColorWhite;
					int parentColorOpacity = 256;
					if (this.Parent != null && this.Parent is IPKRgba && ((IPKRgba)this.Parent).CascadeColorEnabled)
					{
						parentColor = ((IPKRgba)this.Parent).DisplayedColor;
						parentColorOpacity = ((IPKRgba)this.Parent).DisplayedColorOpacity;
					}
					this.UpdateDisplayedColor(parentColor, parentColorOpacity);
				}
			}
		}

		/// <summary>
		/// Color of PKLayerRGBA.
		/// </summary>
		public ushort Color
		{
			get
			{
				return this.realColor;
			}
			set
			{
				this.DisplayedColor = this.realColor = value;

				if (this.CascadeColorEnabled)
				{
					ushort parentColor = PKAlpha.SharedAlpha.ColorWhite;
					int parentColorOpacity = 256;
					if (this.Parent != null && this.Parent is IPKRgba && ((IPKRgba)this.Parent).CascadeColorEnabled)
					{
						parentColor = ((IPKRgba)this.Parent).DisplayedColor;
						parentColorOpacity = ((IPKRgba)this.Parent).DisplayedColorOpacity;
					}
					this.UpdateDisplayedColor(parentColor, parentColorOpacity);
				}
			}
		}

		/// <summary>
		/// Displayed Opacity.
		/// </summary>
		public int DisplayedOpacity { get; private set; }

		/// <summary>
		/// Displayed ColorOpacity.
		/// </summary>
		public int DisplayedColorOpacity { get; private set; }

		/// <summary>
		/// Displayed Color.
		/// </summary>
		public ushort DisplayedColor { get; private set; }

		/// <summary>
		/// Whether or not opacity should be propagated to its children.
		/// </summary>
		public bool CascadeOpacityEnabled { get; set; }

		/// <summary>
		/// Whether or not color should be propagated to its children.
		/// </summary>
		public bool CascadeColorEnabled { get; set; }
		#endregion

		#region Initialization
		public PKLayerRgba()
			: base()
		{
			this.DisplayedColor = this.Color = PKAlpha.SharedAlpha.ColorWhite;
			this.DisplayedOpacity = this.Opacity = this.DisplayedColorOpacity = this.ColorOpacity = 256;
			this.CascadeOpacityEnabled = this.CascadeColorEnabled = false;
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Recursive method that updates display opacity.
		/// </summary>
		/// <param name="parentOpacity"></param>
		public void UpdateDisplayedOpacity(int parentOpacity)
		{
			this.DisplayedOpacity = realOpacity * parentOpacity / 256;

			if (this.CascadeOpacityEnabled)
			{
				foreach (PKNode child in this.Children)
				{
					if (child is IPKRgba)
					{
						((IPKRgba)child).UpdateDisplayedOpacity(this.DisplayedOpacity);
					}
				}
			}
		}

		/// <summary>
		/// Recursive method that updates display color.
		/// </summary>
		public void UpdateDisplayedColor(ushort parentColor, int parentColorOpacity)
		{
			this.DisplayedColorOpacity = parentColorOpacity + realColorOpacity * (256 - parentColorOpacity) / 256;
			this.DisplayedColor = PKAlpha.SharedAlpha.PixelAlpha(this.realColor, parentColor, realColorOpacity);

			if (this.CascadeColorEnabled)
			{
				foreach (PKNode child in this.Children)
				{
					if (child is IPKRgba)
					{
						((IPKRgba)child).UpdateDisplayedColor(this.DisplayedColor, this.DisplayedColorOpacity);
					}
				}
			}
		}
		#endregion
	}

	/// <summary>
	/// PKLayerColor is a subclass of PKLayer that has the color and opacity properties.
	/// <remarks>This is NOT a IPKRgba object.</remarks>
	/// </summary>
	public class PKLayerColor : PKLayer
	{
		#region Private Fields
		private ushort realColor;
		private int realOpacity;
		#endregion

		#region Public Properties
		/// <summary>
		/// Opacity of PKLayerColor.
		/// Range: 0 ~ 256
		/// </summary>
		public int Opacity
		{
			get
			{
				return this.realOpacity;
			}
			set
			{
				if (value < 0)
				{
					value = 0;
				}
				else if (value > 256)
				{
					value = 256;
				}
				this.realOpacity = value;
			}
		}

		/// <summary>
		/// Color of PKLayerColor.
		/// </summary>
		public ushort Color
		{
			get
			{
				return this.realColor;
			}
			set
			{
				this.realColor = value;
			}
		}
		#endregion

		#region Initialization
		public PKLayerColor(ushort color)
			: base()
		{
			this.Color = color;
			this.Opacity = 256;
		}

		public PKLayerColor(ushort color, Size size)
			: this(color)
		{
			this.ContentSize = size;
		}

		public PKLayerColor(ushort color, int width, int height)
			: this(color)
		{
			this.ContentSize = new Size(width, height);
		}
		#endregion

		#region Public Methods
		public override void Draw()
		{
			PKMainSurface.SharedMainSurface.FillAlpha(this.BoundingBox, this.Color, this.Opacity);
		}
		#endregion
	}
}
